/******************************************************************
 * @version      : 1.0
 * @Author       : zhl
 * @Date         : 2023-05-04 18:04:05
 * @LastEditors  : zhl
 * @E-Mail       : zhll168@163.com
 * @Description  : 上传日志文件
 ******************************************************************/
#include "FileUpload.h"
#include "JsonManager.h"
#include "CMosquittoManager.h"
#include "curl/curl.h"
#include "common.h"
#include "Base64.hpp"
#include <stdlib.h>
#include <thread>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/time.h>

#define BUF_LEN (2*1024*1024)
#define DEVICE_TO_CLOUD_TOPIC(sn) "d2c/"+sn+"/command/default/v1"

FileUpload FileUpload::c_instance;

FileUpload::FileUpload(/* args */)
{
  c_packet_num = 1;
  c_buf = (uint8_t *)malloc(BUF_LEN);
  
}

FileUpload::~FileUpload()
{
  if(NULL != c_buf){
    free(c_buf);
  }
}

void FileUpload::init(std::shared_ptr<CMosquittoManager> mm, char* sn){
  c_mm = mm;
  if(NULL == sn){
    snprintf(c_sn, sizeof(c_sn), "%s", "0000000000000000");
  } else {
    snprintf(c_sn, sizeof(c_sn), "%s", sn);
  }
  init_thread();
}

std::string FileUpload::get_file_name(std::string &filename){
    struct timeb tv;
    struct tm *t;
    ftime(&tv);
    t = localtime(&tv.time);
    char str_t[64] = {0};

    snprintf(str_t, sizeof(str_t), "/tmp/pemd/%d%02d%02d%02d%02d%02d%03u.tar.gz", 1900+t->tm_year, 1+t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv.millitm);
    filename = str_t;
    return filename;
}

FileUpload& FileUpload::getInstance(){
  return c_instance;
}

char* FileUpload::get_sn(char *sn, int len){
  if((NULL == sn) || (16 > len)){
    return (char *)"T000000000000000";
  }
  if(1 > get_dev_sn(sn, len)){
      return (char *)"T111111111111111";
  }
  return sn;
}

int FileUpload::compress_file(int type, std::string fname){
  
  std::string cmd = "";
  switch(type){
    case 1:
      cmd = "if [ ! -d /tmp/pemd ]; then mkdir -p /tmp/pemd; fi && rm -rf /tmp/pemd/* && cp -f /data/app/pemd/log/* /tmp/pemd/ && cd /tmp/pemd && tar -zcf "+fname+" *";
    break;
    case 2:
      cmd = "if [ ! -d /tmp/pemd ]; then mkdir -p /tmp/pemd; fi && rm -rf /tmp/pemd/* && cp -f /data/app/pemd/data/protocol_rule.db /tmp/pemd/ && cd /tmp/pemd && tar -zcf "+fname+" *";
    break;
    default:
      cmd = "if [ ! -d /tmp/pemd ]; then mkdir -p /tmp/pemd; fi && rm -rf /tmp/pemd/* && cp -f /data/app/pemd/log/* /tmp/pemd/ && cp -f /data/app/pemd/data/protocol_rule.db /tmp/pemd/ && cd /tmp/pemd && tar -zcf "+fname+" *";
    break;
  }
  int ret = system(cmd.c_str());
  if(0 == ret){
    if(access(fname.c_str(), F_OK) == -1){
      CLogger::getInstance()->log_w("不存在日志文件");
      return -1;
    }
  } else {
    CLogger::getInstance()->log_w("压缩文件失败");
  }
  return ret;
}
int FileUpload::get_packate_total(std::string fname){
  if(fname.empty()){
    return 0;
  }
  int log_file_size = getFileSize(fname.c_str());
  if(0 == log_file_size % BUF_LEN){
    c_packet_total = log_file_size / BUF_LEN;
  } else {
    c_packet_total = (log_file_size / BUF_LEN) + 1;
  }
  return c_packet_total;
}
FILE *FileUpload::get_log_file(int type){
  get_file_name(c_filename);
  if(0 == compress_file(type, c_filename)){\
    FILE* file = fopen(c_filename.c_str(),"r"); //以读的方式打开文件
    if (file == NULL) { //file为NULL,则打开文件失败,退出程序
      CLogger::getInstance()->log_w("日志文件读取失败\n");
      return NULL;
    }
    return file;
  }
  return NULL;
}

void FileUpload::upload_thread(FileUpload *pfup){
  //
  bool is_uploadding = false;
  FILE *fp = NULL;
  int wait_count = 0;
  while(true){
    if(false == is_uploadding){
      pfup->c_thread_notify.lock();
      if(pfup->c_type.empty()){
        pfup->c_thread_notify.wait();
      }
      if(NULL != fp){
        fclose(fp);
        fp = NULL;
      }
      pfup->c_packet_num = 1;
      pfup->c_packet_total = 0;
      fp = pfup->get_log_file(pfup->c_type.front());
      pfup->get_packate_total(pfup->c_filename);
      pfup->c_type.pop_front();
      if(1 > pfup->report_log(fp)){
        is_uploadding = false;
      }
      pfup->c_thread_notify.unlock();
      is_uploadding = true;
      continue;
      
    }
    //加入长度小于0认为本次日志上传完毕。
    pfup->c_upload_notify.lock();
    if(0 == pfup->c_upload_notify.timewait(10000)){
      if(1 > pfup->report_log(fp)){
        is_uploadding = false;
      }
      wait_count = 0;
    } else {
        wait_count++;
        if(2 < wait_count){
          wait_count = 0;
          is_uploadding = false;
        }
    }
    pfup->c_upload_notify.unlock();
  }
}
// 通过stat结构体 获得文件大小，单位字节
size_t FileUpload::getFileSize(const char *fileName){

	if (fileName == NULL) {
		return 0;
	}
	// 这是一个存储文件(夹)信息的结构体，其中有文件大小和创建时间、访问时间、修改时间等
	struct stat statbuf;
	// 提供文件名字符串，获得文件属性结构体
	stat(fileName, &statbuf);
	// 获取文件大小
	size_t filesize = statbuf.st_size;
	return filesize;
}
std::string FileUpload::get_file_name_no_path(std::string fname){
  if(fname.empty()){
    return "";
  }
  std::string::size_type pos = fname.find_last_of('/') + 1;
	std::string filename = fname.substr(pos, fname.length() - pos);
	return filename;
}

int FileUpload::report_log(FILE *fp){
  if(NULL == fp){
    return -1;
  }

  size_t size=fread(c_buf,1,BUF_LEN,fp); 
  if(0 == size){
    c_packet_num = 0;
    c_packet_total = 0;
    return size;
  }
  int content_len = size*2;
  char *content = (char *)malloc(content_len);
  bzero(content, content_len);
  //发送数据
  REPORT_LOG_REQ_OR_REP data;
  data.command = "CMD_REPORT_LOG";
  data.command_id = c_req_id;
  data.command_type = "REQ";
  data.sn = c_sn;
  data.timestamp = get_timestamp();
  if(0 < base64_encode((char *)c_buf, size, content, content_len)){
    data.data.content = content;
  } else {
    data.data.content = "";
  }
  data.data.file_name = get_file_name_no_path(c_filename);
  data.data.packet_num = c_packet_num;
  data.data.file_type = "tar.gz";
  data.data.packet_total_num = c_packet_total;
  std::string tmp_content =JsonManager::getInstance().get_report_log_msg(data);
  free(content);
  CLogger::getInstance()->LOG_I("发送第%d/%d包日志内容", c_packet_num, c_packet_total);
  if(nullptr == c_mm){
    return -1;
  }
  std::string sn = c_sn;
  std::string topic = DEVICE_TO_CLOUD_TOPIC(sn);
  content_len = tmp_content.length();
  c_mm->publish(topic.c_str(), content_len, (const void *)tmp_content.c_str());
  return content_len;
}

void FileUpload::init_thread(){
  std::thread uploadThread(upload_thread, this);
  uploadThread.detach();
}
void FileUpload::start_signal(){
  c_thread_notify.lock();
  c_thread_notify.signal();
  c_thread_notify.unlock();
}
void FileUpload::get_signal(){
  c_upload_notify.lock();
  c_upload_notify.signal();
  c_upload_notify.unlock();
}
//
int FileUpload::upload_log_file(int type, uint64_t req_id){
  c_thread_notify.lock();
  c_type.push_back(type);
  c_req_id = req_id;
  c_thread_notify.signal();
  c_thread_notify.unlock();
  // get_signal();
  return 0;
}

int FileUpload::get_packet(int packet_num, uint64_t req_id){
  c_upload_notify.lock();
  c_packet_num = packet_num;
  c_req_id = req_id;
  c_upload_notify.signal();
  c_upload_notify.unlock();
  return 0;
}